'''
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    @author: Jordi Vilaplana
'''

from BeautifulSoup import BeautifulSoup
import re
import sys
import urllib2

# Arrays that will keep the local cache
movie_list = []
actor_list = []

# "Constant" declarations
URL = "http://www.imdb.com"
MAIN_MENU = "\n1. Find movies.\n2. Find actor.\n3. Browse movie cache.\n4" + \
        ". Browse actor cache.\n5. Exit\n"
ACTOR_MENU = "\tWhich details would you want to see?\n\t1. Filmography.\n" + \
        "\t2. None."
MOVIE_MENU = "\tWhich details would you want to see?\n\t1. Cast list.\n\t" + \
        "2. Keywords.\n\t3. None."

class Movie(object):
    "Class that represents a movie."
    def __init__(self, link, title):
        self.link = link
        self.title = title

    def __getattr__(self, att):
        if(att == "keywords"):
            soup = fetch_source(self.link)
            result = soup.findAll('a', href=re.compile(r"^/keyword/"))
            keywords = []
            for tag in result:
                for content in tag.contents:
                    keywords.append(content)
            self.keywords = keywords
            return self.keywords
        print "\t[Working",
        if(att == "cast_list"):
            soup = fetch_source(self.link)
            cast_list = soup.find('table', { "class" : "cast_list" })
            soup = BeautifulSoup(str(cast_list))
            cast_list = soup.findAll('a', href=re.compile(r"^/name/nm[0-9]*/$"))
            self.cast_list = []
            cast_list = list(set(cast_list))
            for actor in cast_list:
                sys.stdout.write(".")
                actor_link = str(actor).split("\"")[1]
                # Possible useless characters are removed from the string
                actor_link = actor_link[0:17]
                self.cast_list.append(fetch_actor(actor_link))
                add_actor(actor_link)
            print " done!]"
            return self.cast_list

class Actor(object):
    "Class that represents an actor."
    def __init__(self, link, name):
        self.link = link
        self.name = name
    
    def __getattr__(self, att):
        if(att == "filmography"):
            soup = fetch_source(self.link)
            results = soup.find('div', style="display:block;")
            soup = BeautifulSoup(str(results))
            results = soup.findAll('a', href=re.compile(r"^/title/tt[0-9]*/$"))
            self.filmography = []
            for result in results:
                result = str(result)
                aux = result.split("\"")
                movie_link = URL + aux[1]
                movie_title = aux[2]
                movie_title = movie_title.replace("</a>", "")
                movie_title = movie_title.replace(">", "")
                movie = Movie(movie_link, movie_title)
                movie_list.append(movie)
                self.filmography.append(movie)
            return self.filmography

def fetch_source(url):
    "Gets the source code of the given URL"
    url = urllib2.urlopen(url)
    source = url.read()
    return BeautifulSoup(source)

def find_movies():
    "Searches for a movie in the IMDB webpage."
    movie_title = raw_input("Movie title: ")
    movie_title = str(movie_title).replace(" ", "+")
    soup = fetch_source(URL + "/search/title?title=" + movie_title + "&title_type=feature,tv_movie")
    filmlinks = soup.findAll('a', href=re.compile(r"^/title/tt[0-9]*/$"))

    '''
    Eliminar duplicats:
    http://docs.python.org/faq/programming.html#how-do-you-remove-duplicates-from-a-list
    '''
    filmlinks = list(set(filmlinks))
    
    print "Searching movie " + movie_title + " in IMDB...\n"
    for filmlink in filmlinks:
        test = str(filmlink).split("\"")[1]
        # Possible useless characters are removed from the string
        test = test[0:17]
        soup = fetch_source(URL + test)
    
        filmlink = str(soup.find('h1')).split("\n")[1]
        peli = Movie(URL + test, filmlink)

        # Duplicate detection
        new = True
        for movie in movie_list:
            if(peli.link == movie.link):
                new = False
        if(new):
            print "[+] " + peli.title + " (" + peli.link + ")"
            movie_list.append(peli)
        
    print "\nDone!\nYou have " + str(len(movie_list)) + " movies in the cache."

def find_actor():
    "Searches for an actor in the IMDB webpage."
    actor_name = raw_input("Actor name: ")
    actor_name = str(actor_name).replace(" ", "+")
    soup = fetch_source(URL + "/search/name?name=" + actor_name)
    actor_links = soup.findAll('a', href=re.compile(r"^/name/nm[0-9]*/$"))
    
    '''
    Eliminar duplicats:
    http://docs.python.org/faq/programming.html#how-do-you-remove-duplicates-from-a-list
    '''
    actor_links = list(set(actor_links))
    print "Searching actor " + actor_name + " in IMDB..."
    print ""
    for actor_link in actor_links:
        test = str(actor_link).split("\"")[1]
        # Possible useless characters are removed from the string
        test = test[0:17]
        soup = fetch_source(URL + test)
    
        actor_link = str(soup.find('h1')).split("\n")[0].replace("<h1 class=\"header\">", "")
        actor = Actor(URL + test, actor_link)
        
        # Duplicate detection
        new = True
        for a in actor_list:
            if(a.link == actor.link):
                new = False
        if(new):
            print "[+] " + actor.name + " (" + actor.link + ")"
            actor_list.append(actor)
        
    print "\nDone!\nYou have " + str(len(actor_list)) + " actors in the cache."

def browse_movie_cache():
    i = 0
    print "Movie cache:\n------------"
    for movie in movie_list:
        print str(i) + ". " + movie.title + " (" + movie.link + ")"
        i = i + 1
    print "------------"
    option = raw_input("Choose one to see details (q to quit): ")
    if(option == "q"):
        pass
    else:
        try:
            show_movie_details(int(option))
        except ValueError:
            print "Error: Selected index (" + option + ") is not valid!"
        except IndexError:
            print "Error: Selected index (" + option + ") doesn't exist!"
    
def browse_actor_cache():
    i = 0
    print "Actor cache:\n------------"
    for actor in actor_list:
        print str(i) + ". " + actor.name + " (" + actor.link + ")"
        i = i + 1
    print "------------"
    option = raw_input("Choose one to see details (q to quit): ")
    if(option == "q"):
        return
    else:
        show_actor_details(int(option))

def show_filmography(actor):
    print "\tFilmography:"
    for movie in actor.filmography:
        print "\t[+] " + movie.title + " (" + movie.link + ")"

def show_actor_details(index):
    try:
        actor = actor_list[index]
    except IndexError:
        raise
    ACTOR_ACTIONS = { "1": show_filmography }
    while True:
        print actor.name + " (" + actor.link + ")"
        print ACTOR_MENU
        option = raw_input("\tChoose an option: ")
        if(option == "2"):
            print ""
            return
        action = ACTOR_ACTIONS.get(option, invalid_option)
        action(actor)

def show_cast_list(movie):
    print "\tCast list:"
    for actor in movie.cast_list:
        print "\t[+] " + actor.name + " (" + actor.link + ")"
    print ""
        
def show_keywords(movie):
    print "\tKeywords:"
    for keyword in movie.keywords:
        print "\t[+] " + keyword
    print ""

def show_movie_details(index):
    try:
        movie = movie_list[index]
    except IndexError:
        raise
    MOVIE_ACTIONS = { "1": show_cast_list, "2": show_keywords }
    while True:
        print movie.title + " (" + movie.link + ")"
        print MOVIE_MENU
        option = raw_input("\tChoose an option: ")
        if(option == "3"):
            print ""
            return
        action = MOVIE_ACTIONS.get(option, invalid_option)
        action(movie)

def add_actor(link):
    new = True
    for actor in actor_list:
        if(actor.link == link):
            new = False
    if(new):
        actor_list.append(fetch_actor(link))

def get_actor(link):
    for actor in actor_list:
        if(actor.link == URL + link):
            return actor

def fetch_actor(link):
    soup = fetch_source(URL + link)
    for tag in soup.findAll(True):
        if tag.name == "span":
            tag.extract()
    name = soup.find('h1', { "class" : "header" })
    name = name.renderContents()
    name = name.rstrip()
    return Actor(URL + link, name)

def invalid_option():
    "If the user selected an invalid option, is asked for it again"
    print "\nSorry, what did you say?"

def main():
    print "IMDB Web Scrapper\n\tHave fun!\n"
    MAIN_ACTIONS = { "1": find_movies, "2": find_actor,
                    "3": browse_movie_cache, "4": browse_actor_cache }
    while True:
        print MAIN_MENU
        option = raw_input("Choose an option: ")
        if "5" == option:
            print "\nBye bye!"
            return
        action = MAIN_ACTIONS.get(option, invalid_option)
        action()

if __name__ == "__main__":
    main()